package org.dftproject.lineagelinkage.ui.views;

import java.util.LinkedList;
import java.util.List;

import org.dftproject.genesis.data.genealogy.GenealogyConstants;
import org.dftproject.genesis.data.genealogy.GenealogyUtils;
import org.dftproject.genesis.data.genealogy.IName;
import org.dftproject.genesis.data.genealogy.IPerson;
import org.dftproject.genesis.data.genealogy.Sex;
import org.dftproject.genesis.ui.SharedImages;
import org.dftproject.genesis.ui.pages.PageManager;
import org.dftproject.genesis.ui.pages.summary.SummaryPage;
import org.dftproject.genesis.ui.pages.summary.SummaryPageInput;
import org.dftproject.lineagelinkage.core.DuplicateManager;
import org.dftproject.lineagelinkage.core.IDuplicateManagerListener;
import org.dftproject.lineagelinkage.metric.IPedigreeMetric;
import org.dftproject.lineagelinkage.metric.PedigreeMetric;
import org.dftproject.lineagelinkage.metric.PedigreeScore;
import org.dftproject.lineagelinkage.ui.pages.MergePage;
import org.dftproject.lineagelinkage.ui.pages.MergePageInput;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import com.eekbom.utils.Strings;

public class DuplicatesView extends ViewPart {

	public static final String ID = DuplicatesView.class.getName();

	private TreeViewer viewer;
	
	private final IPedigreeMetric pedigreeMetric = new PedigreeMetric();

	@Override
	public void createPartControl(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		TreeColumnLayout layout = new TreeColumnLayout();
		container.setLayout(layout);

		Tree tree = new Tree(container, SWT.FULL_SELECTION);
		tree.setLinesVisible(true);

		TreeColumn column = new TreeColumn(tree, SWT.NONE);
		layout.setColumnData(column, new ColumnWeightData(1));

		column = new TreeColumn(tree, SWT.NONE);
		layout.setColumnData(column, new ColumnWeightData(2));

		viewer = new TreeViewer(tree);
		viewer.setContentProvider(new ITreeContentProvider() {

			private final IDuplicateManagerListener listener = new IDuplicateManagerListener() {

				public void duplicateAdded(final IPerson person, final IPerson duplicate) {
					Display.getDefault().asyncExec(new Runnable() {

						public void run() {
							if (!viewer.getTree().isDisposed())
								viewer.add(getSite(), person);
						}

					});
				}

				public void duplicateRemoved(final IPerson person, final IPerson duplicate) {
					Display.getDefault().asyncExec(new Runnable() {

						public void run() {
							if (!viewer.getTree().isDisposed())
								viewer.remove(duplicate);
						}

					});
				}

			};

			public Object[] getChildren(Object parentElement) {
				if (parentElement == getSite())
					return DuplicateManager.getDefault().getPrimaryPersons().toArray();
				if (parentElement instanceof IPerson) {
					List<Duplicate> duplicates = new LinkedList<Duplicate>();
					for (IPerson duplicate : DuplicateManager.getDefault().getPotentialDuplicates((IPerson) parentElement)) {
						PedigreeScore score = pedigreeMetric.comparePedigrees((IPerson) parentElement, duplicate);
						duplicates.add(new Duplicate((IPerson) parentElement, duplicate, score == null ? 5 : score.getStars()));
					}
					return duplicates.toArray();
				}
				return null;
			}

			public Object getParent(Object element) {
				if (element instanceof IPerson)
					return getSite();
				if (element instanceof Duplicate)
					return ((Duplicate) element).getPrimary();
				return null;
			}

			public boolean hasChildren(Object element) {
				if (element == getSite())
					return DuplicateManager.getDefault().getDuplicateCount() > 0;
				if (element instanceof IPerson)
					return !DuplicateManager.getDefault().getPotentialDuplicates((IPerson) element).isEmpty();
				return false;
			}

			public Object[] getElements(Object inputElement) {
				return DuplicateManager.getDefault().getPrimaryPersons().toArray();
			}

			public void dispose() {
			}

			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
				if (oldInput != null)
					DuplicateManager.getDefault().removeListener(listener);
				if (newInput != null)
					DuplicateManager.getDefault().addListener(listener);
			}

		});
		viewer.setLabelProvider(new ITableLabelProvider() {

			public Image getColumnImage(Object element, int columnIndex) {
				if (columnIndex == 0) {
					IPerson person = null;
					if (element instanceof IPerson)
						person = (IPerson) element;
					else if (element instanceof Duplicate)
						person = ((Duplicate) element).getDuplicate();
					if (person != null) {
						Sex sex = (Sex) GenealogyUtils.getValue(person, GenealogyConstants.sex, Sex.class);
						if (sex == Sex.Male)
							return SharedImages.getDefault().getImage(SharedImages.IMG_MALE);
						else if (sex == Sex.Female)
							return SharedImages.getDefault().getImage(SharedImages.IMG_FEMALE);
					}
				} else if (columnIndex == 1) {
					if (element instanceof Duplicate) {
						
					}
				}

				return null;
			}

			public String getColumnText(Object element, int columnIndex) {
				if (element instanceof IPerson) {
					if (columnIndex == 0) {
						int count = DuplicateManager.getDefault().getPotentialDuplicates((IPerson) element).size();

						IName name = (IName) GenealogyUtils.getValue((IPerson) element, GenealogyConstants.name, IName.class);
						if (name != null)
							return GenealogyUtils.stringFromName(name) + " (" + count + ")";
						return "(unknown)" + " (" + count + ")";
					}
				} else if (element instanceof Duplicate) {
					if (columnIndex == 0) {
						IName name = (IName) GenealogyUtils.getValue(((Duplicate) element).getDuplicate(), GenealogyConstants.name, IName.class);
						if (name != null)
							return GenealogyUtils.stringFromName(name);
					} else if (columnIndex == 1) {
						int stars = ((Duplicate) element).getStars();
						return stars == 1 ? stars + " star" : stars + " stars";
					}
				}

				return null;
			}

			public boolean isLabelProperty(Object element, String property) {
				return false;
			}

			public void addListener(ILabelProviderListener listener) {
			}

			public void removeListener(ILabelProviderListener listener) {
			}

			public void dispose() {
			}

		});
		viewer.setComparator(new ViewerComparator() {
			
			@Override
			public int compare(Viewer viewer, Object e1, Object e2)  {
				if (e1 instanceof IPerson && e2 instanceof IPerson) {
					IName n1 = (IName) GenealogyUtils.getValue((IPerson) e1, GenealogyConstants.name, IName.class);
					IName n2 = (IName) GenealogyUtils.getValue((IPerson) e2, GenealogyConstants.name, IName.class);
					if (n1 == null && n2 == null)
						return 0;
					if (n1 == null)
						return 1;
					if (n2 == null)
						return -1;
					return Strings.compareNatural(GenealogyUtils.stringFromName(n1), GenealogyUtils.stringFromName(n2));
				}
				
				if (e1 instanceof Duplicate && e2 instanceof Duplicate) {
					int c = ((Duplicate) e2).getStars() - ((Duplicate) e1).getStars();
					if (c != 0)
						return c;
					
					IName n1 = (IName) GenealogyUtils.getValue(((Duplicate) e1).getDuplicate(), GenealogyConstants.name, IName.class);
					IName n2 = (IName) GenealogyUtils.getValue(((Duplicate) e2).getDuplicate(), GenealogyConstants.name, IName.class);
					if (n1 == null && n2 == null)
						return 0;
					if (n1 == null)
						return 1;
					if (n2 == null)
						return -1;
					return Strings.compareNatural(GenealogyUtils.stringFromName(n1), GenealogyUtils.stringFromName(n2));
				}
				
				return 0;
			}
			
		});
		viewer.addDoubleClickListener(new IDoubleClickListener() {

			public void doubleClick(DoubleClickEvent event) {
				ISelection selection = event.getSelection();
				if (selection instanceof IStructuredSelection) {
					Object element = ((IStructuredSelection) selection).getFirstElement();
					if (element instanceof IPerson) {
						IPerson person = ((IPerson) element);

						IWorkbench workbench = PlatformUI.getWorkbench();
						IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
						IWorkbenchPage workbenchPage = window.getActivePage();
						PageManager.getDefault().openPage(workbenchPage, new SummaryPageInput(person), SummaryPage.ID, true, false);
					} else if (element instanceof Duplicate) {
						IWorkbench workbench = PlatformUI.getWorkbench();
						IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
						IWorkbenchPage workbenchPage = window.getActivePage();
						PageManager.getDefault().openPage(workbenchPage, new MergePageInput(((Duplicate) element).getPrimary(), ((Duplicate) element).getDuplicate()), MergePage.ID, true, false);
					}
				}
			}

		});
		viewer.setInput(this.getViewSite());
		
		Menu contextMenu = new Menu(viewer.getTree());

		final MenuItem viewSummary = new MenuItem(contextMenu, SWT.NONE);
		viewSummary.setText("View Person Summary");
		viewSummary.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent event) {
				ISelection selection = viewer.getSelection();
				if (selection instanceof IStructuredSelection) {
					Object element = ((IStructuredSelection) selection).getFirstElement();
					if (element instanceof IPerson) {
						IPerson person = ((IPerson) element);

						IWorkbench workbench = PlatformUI.getWorkbench();
						IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
						IWorkbenchPage workbenchPage = window.getActivePage();
						PageManager.getDefault().openPage(workbenchPage, new SummaryPageInput(person), SummaryPage.ID, true, false);
					} else if (element instanceof Duplicate) {
						IPerson person = ((Duplicate) element).getDuplicate();

						IWorkbench workbench = PlatformUI.getWorkbench();
						IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
						IWorkbenchPage workbenchPage = window.getActivePage();
						PageManager.getDefault().openPage(workbenchPage, new SummaryPageInput(person), SummaryPage.ID, true, false);
					}
				}
			}
			
		});

		viewer.getTree().setMenu(contextMenu);
	}

	@Override
	public void setFocus() {
		viewer.getTree().setFocus();
	}

	protected class Duplicate {

		private final IPerson primary;
		private final IPerson duplicate;
		private final int stars;

		public Duplicate(IPerson primary, IPerson duplicate, int stars) {
			this.primary = primary;
			this.duplicate = duplicate;
			this.stars = stars;
		}

		public IPerson getPrimary() {
			return primary;
		}

		public IPerson getDuplicate() {
			return duplicate;
		}
		
		public int getStars() {
			return stars;
		}

	}

}
